﻿/************************************************************************************************************
//CamButton Class ver 1.0 erstellt am 14. Juni 2007
//
// Diese Klasse bearbeitet das Webcambild und verwaltet die Funktionen der CamButtons. Die Klasse überprüft ob ein
// ein Button aktiv ist oder nicht und führt die dementsprechende Ereignisfunktion aus
//
// Author: Florian Weil --- http://www.derhess.de
//
// Die Klasse kann frei für nicht kommerzielle Zwecke verwendet werden. 
// Bei kommerzieller Verwendung bitte eine kurze Mail an info@derhess.de 
//
/************************************************************************************************************/

import flash.display.BitmapData;
import flash.filters.BlurFilter;

import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;

import de.derhess.iaCam.CamButton;


class de.derhess.iaCam.CamButtonManager {

	// Statische Variablen fuer den Modus der Bewegungserkennung
	static public var STATIC = 0;
	static public var DYNAMIC = 1;
	
	//Konfigurationsmembers
	private var mode:Number // Speichern des Analysemodus - STATIC oder DYNAMIC
	private var accuracy:Number // Zahl in Prozent bis ein Ereigniss im Button ausgelöst wird
	
	// Enthält das WebcamSignal
	private var webcamContainer:MovieClip;
	private var scaleFactor:Number // gibt an um welchen Wert das Bilder herunterskaliert wird -> Perfomance
	
	// Speicherung der Bilddaten für die Bildanalyse
	private var snapshot:BitmapData;
	private var output:BitmapData;
	
	// Zwischenspeicherung von Bilddaten für dynamischen Modus
	private var difference:BitmapData;
	private var oldFrame:BitmapData
	
	private var webcamFrames:Array;
	private var quantityFrames:Number
	// Verwaltung aller Buttonliste
	private var buttonlist:Array;
	
	
	// Konstruktor
	public function CamButtonManager(webcamSignal:MovieClip,camMode:Number) {
		
		this.webcamContainer = webcamSignal;
		this.accuracy = 80; // Standardwert 80
		this.mode = camMode;
		this.quantityFrames = 5; // Standardwert 5
		
		var standardSize:Number = 80;
		// Perfomance -> Skalierung der Analysebildes auf 80x60px
		this.scaleFactor = Math.round(100/(this.webcamContainer._width  / standardSize));  
		
		this.webcamFrames = new Array();
		this.buttonlist = new Array();
		this.output =	new BitmapData(this.webcamContainer._width/(100/this.scaleFactor),this.webcamContainer._height/(100/this.scaleFactor),true,0xFF000000);
		this.snapshot = new BitmapData(this.webcamContainer._width/(100/this.scaleFactor),this.webcamContainer._height/(100/this.scaleFactor),true,0xFF000000);
		this.difference = new BitmapData(this.webcamContainer._width/(100/this.scaleFactor),this.webcamContainer._height/(100/this.scaleFactor),true,0xFF000000);
		this.oldFrame = new BitmapData(this.webcamContainer._width/(100/this.scaleFactor),this.webcamContainer._height/(100/this.scaleFactor),true,0xFF000000);
	}
	
	
	// Buttons hinzufüegen
	public function createCamButton(xpos:Number,ypos:Number,width:Number,height:Number):CamButton {
		var pushed:Number = this.buttonlist.push(new CamButton(xpos,ypos,width,height,this.scaleFactor));
		return this.buttonlist[pushed-1];
	}
	
	// Button der Liste hinzufügen
	public function addCamButton(button:CamButton):Boolean {
		//Suche den Button und wenn schon vorhanden gibt false zurück
		for(var i:Number = 0; i < this.buttonlist.length; i++) {
			// evtl. mit den "==" Operator arbeiten
			if(this.buttonlist[i] === button) {
				return false;
			}
		}
		
		// Wenn der Button nicht vorhanden in der Liste, dann füge diesen hinzu
		this.buttonlist.push(button);
		return true;
	}
	
	// Buttons löschen
	public function removeCamButton(button:CamButton):Boolean {
		//Suche den Button und lösche diesen
		for(var i:Number = 0; i < this.buttonlist.length; i++) {
			// evtl. mit den "==" Operator arbeiten
			if(this.buttonlist[i] === button) {
				this.buttonlist.splice(i,1);
				return true;
			}
		}
		return false;
	}
	
	
	// Erstellen eines Anfangsframes für den statischen Analysemodus
	public function doSnapshot():Void {
		
		var scaleMatrix:Matrix = new Matrix();
		scaleMatrix.scale(this.scaleFactor/100,this.scaleFactor/100);

		this.snapshot.draw(this.webcamContainer,scaleMatrix);
		
	}	
	
	// Starten der ButtonAnalyze
	public function startAnalyze():Void {
		if(this.mode == 0) {
			staticImageProcessing();
		} else {
			dynamicImageProcessing();
		}
	
	}
	
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//////////////// GETTER und SETTER Funktionen
	public function getBitmapData():BitmapData {
			return this.output;
	}
	
	public function getMode():Number {
		return this.mode;
	}
	
	// Zahl zwischen 0 und 1 - siehe static Variablen der Klasse
	public function setMode(modus:Number) {
		if(modus == 0)
			this.mode = 0;
		else
			this.mode = 1;
	}
	
	// Wert ab welcher Genauigkeit (in Prozent) der Button als aktiv ausgelöst wird
	public function getAccuracy():Number {
		return this.accuracy;
	}
	// Setzen des Wertes (in Prozent) ab welcher Genauigkeit der Button als aktiv ausgelöst wird
	public function setAccuracy(value:Number):Void {
		if(value > 0 && value < 100) {
			this.accuracy = value;
		} else if(value < 0) {
			this.accuracy = 0;
		} else {
			this.accuracy = 100;
		}
	}
	
	
	public function getQuantityFrames():Number {
		return this.quantityFrames;
	}
	
	public function setQuantityFrames(number:Number):Void {
		this.quantityFrames = number;
	}
	
	
	/*///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///////////////////////////////////////// Hilfsfunktionen //////////////////////////////////////////////////////////////////
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/
	
	// extrahiert den User aus dem Bild  - erstellt das Binärbild
	private function staticImageProcessing()
	{
		var scaleMatrix:Matrix = new Matrix();
		scaleMatrix.scale(this.scaleFactor/100,this.scaleFactor/100);
		
		this.output.draw(this.webcamContainer,scaleMatrix);
		// Differenzverfahren anwenden
		this.output.draw(this.snapshot,new Matrix(),null,"difference");
		
		// Störungen abschwächen -- hier gibt es einige Möglichkeiten die Extrahierung zu verbessern
		var blurX:Number = 2;
		var blurY:Number = 2;
		var quality:Number = 1;
		var filter:BlurFilter = new BlurFilter(blurX, blurY, quality);
		
		this.output.applyFilter(this.output,this.output.rectangle,new Point(0,0),filter);
		
		// erster Farbwert 0xFF111111 ist der Schwellwert - Hier gibt es auch noch einige zusätzliche Möglichkeiten die Extrahierung zu verbessern
		this.output.threshold(this.output,this.output.rectangle,this.output.rectangle.topLeft,">",0xFF222222,0xFFFFFFFF,0x00FFFFFF,true);
		//trace(this.output.getPixel(5,5).toString(16));
		// Nach Interaktion im Bild suchen
		this.analyzeButtons();
	}
	
	private function dynamicImageProcessing()
	{
		//Prinzip siehe MotionDetection Tutorial von Guy Watson - www.flashguru.co.uk
		var scaleMatrix:Matrix = new Matrix();
		scaleMatrix.scale(this.scaleFactor/100,this.scaleFactor/100);
		this.snapshot.draw(webcamContainer,scaleMatrix);
		//copy the current freezeframe
		this.difference =this.snapshot.clone();
		//now draw the previous freezeframe ("BEFORE"!) on top of the current frameframe and apply the 'difference' blendmode
		this.difference.draw(this.oldFrame,new Matrix(),null,"difference");
		//filter out all pixels in the differenced bitmap that are greater than just over black (0xFF111111) and make them white (0xFFFFFFFF)
		//over-write the previous contents of the bitmap
		this.difference.threshold(this.difference,this.difference.rectangle,this.difference.rectangle.topLeft,">",0xFF222222,0xFFFFFFFF,0x00FFFFFF,false);
		//add the finished bitmap to a fading image cue
		this.webcamFrames.push(this.difference.clone());
		//if the cue is now greater than the maximum length we specified
		if(this.webcamFrames.length > this.quantityFrames)
		{
			//then remove the bitmap at the very front of the cue
			//and free the memory it was using
			while(webcamFrames.length > this.quantityFrames) this.webcamFrames.shift().dispose();
		}
		
		//clear the out bitmap object so we can start a fresh
		this.output.fillRect(this.output.rectangle,0xFF000000);
		//copy the current bitmap and save it for the next time this function is called
		this.oldFrame = this.snapshot.clone();
		//loop through each bitmap in the fading image cue
		for(var i=0;i<this.webcamFrames.length;++i) {
			//copy all the pixels that are green in the current item in the fading image cue and make them into the degradated green color
			//add those pixels on top of the bitmap that will displayed to the user
			this.output.threshold(this.webcamFrames[i],this.output.rectangle,this.output.rectangle.topLeft,"==",0xFFFFFFFF,0xFFFFFFFF,0x00FFFFFF,false);
	
		}
		
		// Störungen abschwächen -- hier gibt es einige Möglichkeiten die Extrahierung zu verbessern
		var blurX:Number = 4;
		var blurY:Number = 4;
		var quality:Number = 1;
		var filter:BlurFilter = new BlurFilter(blurX, blurY, quality);
		
		this.output.applyFilter(this.output,this.output.rectangle,new Point(0,0),filter);
		//this.output.threshold(this.output,this.output.rectangle,this.output.rectangle.topLeft,"==",0xFFFFFFFF,0xFFFFFFFF,0x00FFFFFF,false);
		// Nach Interaktion im Bild suchen
		this.analyzeButtons();
	}
	
	
	// Hier wird geschaut ob die Buttons aktiv sind. Dementsprechend werden die Ereignisse geworfen
	private function analyzeButtons():Void {
		// Gehe Alle CamButtons durch und führe die Ereignisse aus
		for(var i:Number = 0; i < this.buttonlist.length; i++) {
			var rect:Rectangle = this.buttonlist[i].getAnalyzeRect();
			
			var activePixel:Number = 0;
			//Aktivität im Button überprüfen
			for(var ypos:Number = 0; ypos < rect.height; ypos++) {
				for(var xpos:Number = 0; xpos < rect.width; xpos++) {
					
					if(this.output.getPixel(rect.x + xpos, rect.y + ypos).toString(16) == "ffffff") {
						activePixel++;
					}
				}
			} // Ende activePixel Analyze
			
			var accuracyNow:Number = Math.round((activePixel / (rect.width * rect.height))*100); 
			
			// Button Aktiv oder nicht - Überprüfen welche Ereignisse ausgeführt werden sollen
			if(accuracyNow >= this.accuracy) {
				if(this.buttonlist[i].getState()) {
					//Wenn die Han schon länger über den Button ist
					this.buttonlist[i].onHandOver();
				} else {
					this.buttonlist[i].setState(true);
					this.buttonlist[i].onHand();
				}
			} else if(this.buttonlist[i].getState()) {
				this.buttonlist[i].setState(false);
				this.buttonlist[i].onHandOff();
			}
		
		} // ButtonSchleife
	
	}// Ende Funkton
} // Ende Klasse